package domain

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"strconv"
	"time"
)

const (
	ClubInviteLinkFieldClub    = "Club"
	ClubInviteLinkFieldInviter = "Inviter"
)

type ClubInviteLink struct {
	ID          int64     `json:"-"`
	InviterID   int64     `json:"-"`
	ClubID      int64     `json:"-"`
	ExpireTime  time.Time `json:"expire_time" `
	CreatedAt   time.Time `json:"created_at" `
	InviteCount int64     `json:"invite_count" gorm:"not null;default:999"`
	Club        Club      `json:"club" gorm:"foreignKey:ClubID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
	Inviter     User      `json:"inviter" gorm:"foreignKey:InviterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
}

type ClubInviteLinkDTO struct {
	ID          int64     `json:"-"`
	InviterID   int64     `json:"-"`
	ClubID      int64     `json:"-"`
	ExpireTime  time.Time `json:"expire_time" `
	CreatedAt   time.Time `json:"created_at" `
	InviteCount int64     `json:"invite_count" gorm:"not null;default:999"`
	Club        ClubDTO   `json:"club" gorm:"foreignKey:ClubID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
	Inviter     UserDTO   `json:"inviter" gorm:"foreignKey:InviterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
}

func (c *ClubInviteLink) ToDTO() (res *ClubInviteLinkDTO) {
	res = &ClubInviteLinkDTO{
		ID:         c.ID,
		InviterID:  c.InviterID,
		ClubID:     c.ClubID,
		ExpireTime: c.ExpireTime,
		CreatedAt:  c.CreatedAt,
	}
	return
}

const clubInviteKey = "club_invite_link"

func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

func AesEncrypt(origData, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	blockSize := block.BlockSize()
	origData = PKCS5Padding(origData, blockSize)
	blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
	crypted := make([]byte, len(origData))
	blockMode.CryptBlocks(crypted, origData)
	return crypted, nil
}

func AesDecrypt(crypted, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	blockSize := block.BlockSize()
	blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
	origData := make([]byte, len(crypted))
	blockMode.CryptBlocks(origData, crypted)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

func EncodeId(id int64) string {
	encrypt, err := AesEncrypt([]byte(strconv.FormatInt(id, 10)), []byte(clubInviteKey))
	if err != nil {
		return ""
	}
	return string(encrypt)
}
func DecodeID(id []byte) int64 {
	decrypt, err := AesDecrypt(id, []byte(clubInviteKey))
	if err != nil {
		return 0
	}
	deId, err := strconv.Atoi(string(decrypt))
	if err != nil {
		return 0
	}
	return int64(deId)
}
